using System;
using System.Collections ; 
using System.Collections.Generic;
using System.IO; 
using System.Text;
using Parseq.Util; 
using Parseq.Core ; 

namespace Parseq.Compiler
{
	/* there is basically a single Lexer2 - in a way it does not make sense to declare it as class-level.
	 * 
	 */
    public class Lexer2
    {
        /* this is a regular expression parser generator. 
         * 
         * Special characters 
         * 
         * ANY : '.' 
         * 
         * ESCAPE : '\' 
         * 
         * ZERO_MANY : '*'
         * 
         * ONE_MANY : '+'
         * 
         * ZERO_ONE : '?'
         * 
         * GROUP_OPEN : '(' 
         * 
         * GROUP_CLOSE : ')'
         * 
         * RANGE_OPEN : '{'
         * 
         * RANGE_CLOSE : '}'
         * 
         * ALTERNATIVE : '|' 
         * 
         * DELIMITER : ',' 
         * 
         * NOT : '~' 
         * 
         * SPECIAL : ANY | ESCAPE | ZERO_MANY | ONE_MANY | ZERO_ONE | GROUP_OPEN | GROUP_CLOSE | RANGE_OPEN | RANGE_CLOSE | ALTERNATIVE | DELIMITER 
         * 
         * CHAR : '\' ANY | ~SPECIAL 
         * 
         * 
         * 
         * nested : '(' CHAR+ ( '|' CHAR+)* ')' 
         * 
         */

        private static readonly BaseParser whitespace =
            Combinator.OneMany(Combinator.CharIn(' ', '\r', '\n', '\t', '\f'))
                .SetTransformArray(delegate(object[] spaces)
                {
                    return new Token("WHITESPACE", spaces);
                });

        private static readonly BaseParser regularChar
            = Combinator.CharNotIn('.', '\\', '*', '+', '?', '(', ')', '{', '}', '|', ',', '~')
                .SetTransform(delegate(object o)
                {
                    return Combinator.CharEquals((char)o);
                });

        private static readonly BaseParser escapedChar
            = Combinator.Sequence(Combinator.CharEquals('\\'), Combinator.CharAny())
                .SetTransformArray(delegate(object[] chars)
                {
                    return Combinator.CharEquals((char)chars[1]);
                });

        private static readonly BaseParser anyChar 
            = Combinator.CharEquals('.').SetTransform(delegate(object o) {
                return Combinator.CharAny(); 
            }); 

        private static readonly BaseParser legalChar
            = Combinator.Choice(
				Combinator.MapCharsTo("\\r", Combinator.CharEquals('\r'))
				, Combinator.MapCharsTo("\\b", Combinator.CharEquals('\b'))
				, Combinator.MapCharsTo("\\n", Combinator.CharEquals('\n'))
				, Combinator.MapCharsTo("\\f", Combinator.CharEquals('\f'))
				, Combinator.MapCharsTo("\\t", Combinator.CharEquals('\t'))
				, Combinator.MapCharsTo("\\s", Combinator.CharEquals(' '))
	            , Combinator.MapCharsTo("\\d", Combinator.CharIsDigit()) 
				, Combinator.MapCharsTo("\\a", Combinator.CharIsLowerAlpha())
				, Combinator.MapCharsTo("\\u", Combinator.CharIsUpperAlpha())
				, Combinator.MapCharsTo("\\A", Combinator.CharIsAlpha())
	            , escapedChar
	            , anyChar
	            , regularChar);
		
        private static readonly BaseParser negateLegalChar
            = Combinator.Sequence(Combinator.CharEquals('~'), legalChar)
                .SetTransformArray(delegate(object[] chars)
            {
                CharParser parser = (CharParser)chars[1];
                return parser.SetToNegate(true); 
            });

        private static readonly BaseParser expression
            = Combinator.Choice(negateLegalChar, legalChar); 
		
		
		private static readonly BaseParser expressionSeq
			= Combinator.OneMany(expression)
                    .SetTransformArray(delegate(object[] parsers) {
                        return Combinator.MakeSequence(Array.ConvertAll<object, BaseParser>(parsers, delegate(object p) { return (BaseParser)p; }));
                    }); 
        private static readonly BaseParser nestedExp
            = Combinator.Sequence(Combinator.CharEquals('(')
                , Combinator.Delimited(expressionSeq, Combinator.CharEquals('|'))
                , Combinator.CharEquals(')'))
            .SetTransformArray(delegate(object[] nested)
            {
				return Combinator.MakeChoice(Array.ConvertAll<object, BaseParser>((object[])nested[1], delegate(object p) { return (BaseParser)p; }));
            });

        private static readonly BaseParser grammar
            = Combinator.OneMany(expression).SetTransformArray(delegate(object[] parsers)
        {
            return Combinator.MakeSequence(Array.ConvertAll<object, BaseParser>(parsers, delegate(object o) { return (BaseParser)o; }));
        });

        private static readonly BaseParser repeatableExp = Combinator.Choice(nestedExp, negateLegalChar, legalChar); 

        /*
         * zeroOne -> exp ?
         * exp => nested | legalChar 
         */
        private static readonly BaseParser zeroOneExp
            = Combinator.Sequence(repeatableExp, Combinator.CharEquals('?'))
            .SetTransformArray(delegate(object[] parsers)
        {
            return Combinator.ZeroOne((BaseParser)parsers[0], "");
        });

        private static readonly BaseParser zeroManyExp
            = Combinator.Sequence(repeatableExp, Combinator.CharEquals('*'))
            .SetTransformArray(delegate(object[] nested)
        {
            return Combinator.ZeroMany((BaseParser)nested[0]); 
        });

        private static readonly BaseParser oneManyExp // this one has a left recursion! 
            = Combinator.Sequence(repeatableExp, Combinator.CharEquals('+'))
            .SetTransformArray(delegate(object[] nested)
            {
                return Combinator.OneMany((BaseParser)nested[0]);
            });

        private static readonly BaseParser integer 
            = Combinator.Sequence(Combinator.ZeroOne(Combinator.Choice(Combinator.CharEquals('+'),Combinator.CharEquals('-')),'+')
                , Combinator.OneMany(Combinator.CharIsDigit()).SetTransformArray(delegate(object[] digits) {
                    return Convert.ToInt64(new string(Array.ConvertAll<object,char>(digits, delegate(object o) { return (char)o; })));
                })).SetTransformArray(delegate(object[] signNumber) {
                    if ((char)(signNumber[0]) == '-') {
                        return (long)signNumber[1] * -1;
                    } else {
                        return signNumber[1];
                    }
                });

        private static readonly BaseParser repeatCount
            = Combinator.Choice(Combinator.Sequence(Combinator.CharEquals(','), integer).SetTransformArray(delegate(object[] range) {
                                    return new long[2] { 0 , (long)range[1] }; 
                                })
                                , Combinator.Sequence(integer, Combinator.CharEquals(','), integer).SetTransformArray(delegate(object[] range) {
                                    return new long[2] { (long)range[0], (long)range[2] }; 
                                })
                                , Combinator.Sequence(integer , Combinator.CharEquals(',')).SetTransformArray(delegate(object[] range) {
                                    return new long[2] { (long)range[0], -1 };
                                })
                                , Combinator.Sequence(integer).SetTransformArray(delegate(object[] range) {
                                    return new long[2] { (long)range[0], (long)range[0] };
                                }));

        private static readonly BaseParser repeatExp
            = Combinator.Sequence(repeatableExp, Combinator.CharEquals('{'), repeatCount , Combinator.CharEquals('}'))
                .SetTransformArray(delegate(object[] range) {
                    return Combinator.Repeat((BaseParser)range[0], ((long[])range[2])[0], ((long[])range[2])[1]);
                }); 

        static Lexer2()
        {
            ((ChoiceParser)expression).Prepend(zeroOneExp);
            ((ChoiceParser)expression).Prepend(zeroManyExp);
            ((ChoiceParser)expression).Prepend(oneManyExp);
            ((ChoiceParser)expression).Prepend(repeatExp); 
            ((ChoiceParser)expression).Add(nestedExp); // allow for nested expression! the whole expression itself is the first level nesting. 
        } 

        public static BaseParser Compile(string tokenType, string exp)
        {
            using (CharReader reader = new CharReader(Util.String.StringToStream(exp)))
            {
                Result result = grammar.Parse(reader, new Location(0));
                if (Result.Succeeded(result))
                {
                    return ((SequenceParser)result.Inner).SetTransformArray(delegate(object[] parsed)
                    {
                        return new Token(tokenType, BaseParser.AtomToString(parsed)); 
                    }); 
                }
                else
                {
                    throw new InvalidDataException(string.Format("Invalid regular expression: {0}", exp));
                }
            }
        }
    }
}
